package com.treasure.mall.biz.service.core.order.impl;

import com.treasure.mall.biz.base.AbstractSearchableCrudService;
import com.treasure.mall.biz.base.SpecificationAndSort;
import com.treasure.mall.biz.dto.BaseSaveDTO;
import com.treasure.mall.biz.dto.BaseSearchDTO;
import com.treasure.mall.biz.dto.condition.BizOrderCondition;
import com.treasure.mall.biz.dto.order.BizOrderCreateDTO;
import com.treasure.mall.biz.dto.user.ModifyDepositDTO;
import com.treasure.mall.biz.entity.auction.AuctionSessionPO;
import com.treasure.mall.biz.entity.live.LiveRoomPO;
import com.treasure.mall.biz.entity.live.LiveSessionPO;
import com.treasure.mall.biz.entity.order.BizOrderPO;
import com.treasure.mall.biz.entity.order.BizOrderPO_;
import com.treasure.mall.biz.entity.pay.PaymentBillPO;
import com.treasure.mall.biz.entity.pay.RefundBillPO;
import com.treasure.mall.biz.entity.user.UserPO;
import com.treasure.mall.biz.entity.user.UserPO_;
import com.treasure.mall.biz.remote.quickpay.WechatPayService;
import com.treasure.mall.biz.remote.quickpay.dto.WxRefundDTO;
import com.treasure.mall.biz.repository.order.BizOrderRepository;
import com.treasure.mall.biz.service.core.auction.AuctionSessionService;
import com.treasure.mall.biz.service.core.live.LiveRoomService;
import com.treasure.mall.biz.service.core.live.LiveSessionService;
import com.treasure.mall.biz.service.core.order.BizOrderService;
import com.treasure.mall.biz.service.core.pay.PaymentBillService;
import com.treasure.mall.biz.service.core.pay.RefundBillService;
import com.treasure.mall.biz.service.core.user.MerchantService;
import com.treasure.mall.biz.service.core.user.UserService;
import com.treasure.mall.zbq.common.Jsr310Utils;
import com.treasure.mall.zbq.common.RandomUtils;
import com.treasure.mall.zbq.common.base.ApiResult;
import com.treasure.mall.zbq.common.base.ResultCodeEnum;
import com.treasure.mall.zbq.common.exception.ApiException;
import com.treasure.mall.zbq.common.ienum.auction.AuctionEnums;
import com.treasure.mall.zbq.common.ienum.base.CodeDescEnumHelper;
import com.treasure.mall.zbq.common.ienum.log.DepositLogType;
import com.treasure.mall.zbq.common.ienum.order.OrderEnums;
import com.treasure.mall.zbq.common.ienum.product.ProductEnums;
import com.treasure.mall.zbq.common.ienum.user.UserEnums;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author allan
 * @version 1.0.0
 * @date 2020/6/14
 */
@Service
@Slf4j
public class BizOrderServiceImpl extends AbstractSearchableCrudService<BizOrderPO, String> implements BizOrderService {
    private BizOrderRepository bizOrderRepository;
    @Autowired
    private UserService userService;
    @Autowired
    private AuctionSessionService auctionSessionService;
    @Autowired
    private RefundBillService refundBillService;
    @Autowired
    private PaymentBillService paymentBillService;
    @Autowired
    private WechatPayService wechatPayService;
    @Autowired
    private MerchantService merchantService;
    @Autowired
    private LiveRoomService liveRoomService;
    @Autowired
    private LiveSessionService liveSessionService;

    @Autowired
    protected BizOrderServiceImpl(BizOrderRepository repository) {
        super(repository);
        this.bizOrderRepository = repository;
    }

    @Override
    protected SpecificationAndSort<BizOrderPO> specificationAndSort(BaseSearchDTO searchDTO) {
        BizOrderCondition condition = (BizOrderCondition) searchDTO;

        Specification<BizOrderPO> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (!StringUtils.isEmpty(condition.getOrderId())) {
                predicates.add(cb.equal(root.get(BizOrderPO_.orderId), condition.getOrderId()));
            }
            if (!StringUtils.isEmpty(condition.getUserKey())) {
                predicates.add(cb.or(
                        cb.like(root.get(BizOrderPO_.userPO).get(UserPO_.nickName), likeAll(condition.getUserKey())),
                        cb.like(root.get(BizOrderPO_.userPO).get(UserPO_.mobile), likeAll(condition.getUserKey()))
                ));
            }
            if (condition.getDealType() != -1) {
                OrderEnums.DealType dealType = CodeDescEnumHelper.getEnumTypeByCode(OrderEnums.DealType.class, condition.getDealType());
                predicates.add(cb.equal(root.get(BizOrderPO_.dealType), dealType));
            }

            if (!StringUtils.isEmpty(condition.getBeginTime())) {
                LocalDateTime beginTime = Jsr310Utils.DateTime.of(condition.getBeginTime());
                predicates.add(cb.greaterThanOrEqualTo(root.get(BizOrderPO_.createTime), beginTime));
            }
            if (!StringUtils.isEmpty(condition.getEndTime())) {
                LocalDateTime endTime = Jsr310Utils.DateTime.of(condition.getEndTime());
                predicates.add(cb.lessThanOrEqualTo(root.get(BizOrderPO_.createTime), endTime));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Sort sort = new Sort(Sort.Direction.DESC, "createTime");

        return new SpecificationAndSort<>(specification, sort);
    }

    @Override
    @Transactional
    public BizOrderPO create(BizOrderCreateDTO createDTO) {
        BizOrderPO bizOrderPO = new BizOrderPO();
        bizOrderPO.setOrderId(RandomUtils.randomFlowNo());
        bizOrderPO.setLiveSessionId(createDTO.getLiveSessionId());
        bizOrderPO.setUserPO(new UserPO(createDTO.getUserId()));
        bizOrderPO.setFinalAmount(createDTO.getFinalAmount());
        bizOrderPO.setSurplusAmount(createDTO.getFinalAmount());
        bizOrderPO.setPayStatus(OrderEnums.PayStatus.TO_PAY);
        bizOrderPO.setDealType(createDTO.getDealType());
        bizOrderPO.setPayType(createDTO.getPayType());

        if (bizOrderPO.getDealType() == OrderEnums.DealType.AUCTION) {
            //拍卖订单记录商品信息
            AuctionSessionPO sessionPO = auctionSessionService.findById(createDTO.getSessionId());
            bizOrderPO.setGoodsId(sessionPO.getProductPO().getProductId());
            bizOrderPO.setGoodsBn("");
            bizOrderPO.setGoodsName(sessionPO.getProductPO().getProductName());
            bizOrderPO.setThumbnail(sessionPO.getProductPO().getMainPic());
            ProductEnums.ProductType productType = CodeDescEnumHelper.getEnumTypeByCode(ProductEnums.ProductType.class, sessionPO.getProductPO().getProductType());
            bizOrderPO.setProductType(productType);
            bizOrderPO.setRoomId(sessionPO.getProductPO().getRoomId());
            if (bizOrderPO.getRoomId() > 0) {
                LiveRoomPO liveRoomPO = liveRoomService.findById(bizOrderPO.getRoomId());
                LiveSessionPO liveSessionPO = liveSessionService.getCurrentSession(bizOrderPO.getRoomId());
                if (liveSessionPO != null) {
                    bizOrderPO.setRoomName(liveRoomPO.getData().getTitle());
                    bizOrderPO.setLiveSessionId(liveSessionPO.getId());
                }
                bizOrderPO.setMerchantPO(liveRoomPO.getMerchant());
            }
            bizOrderPO.setPrice(sessionPO.getProductPO().getPrice());
            bizOrderPO.setAuctionSessionId(sessionPO.getId());
        }
        bizOrderPO.setPaymentBillId(createDTO.getPaymentBillPO().getId());

        bizOrderPO = repository.save(bizOrderPO);

        //支付成功
        payed(bizOrderPO);

        return bizOrderPO;
    }

    @Override
    public void payed(BizOrderPO bizOrderPO) {
        bizOrderPO.setPayStatus(OrderEnums.PayStatus.PAYED);
        bizOrderPO.setPayTime(LocalDateTime.now());

        save(bizOrderPO);

        //支付成功处理业务

        switch (bizOrderPO.getDealType()) {
            case UPGRADE:
                userService.upgradeSuccess(bizOrderPO.getUserPO().getUserId(), bizOrderPO.getOrderId());
                break;
            case RECHARGE:
                // TODO: 2020/6/14 用户充值
                break;
            case DEPOSIT_RECHARGE:
                //保证金充值
                merchantService.modifyDeposit(
                        ModifyDepositDTO.builder()
                                .merchantId(bizOrderPO.getUserPO().getMerchantId())
                                .logType(DepositLogType.RECHARGE)
                                .amount(bizOrderPO.getFinalAmount())
                                .orderId(bizOrderPO.getOrderId())
                                .billId(bizOrderPO.getPaymentBillId())
                                .remark(DepositLogType.RECHARGE.getDesc())
                                .build()
                );
            case AUCTION:
                //判断当前拍卖是否结束，如果结束，发起退款
                AuctionSessionPO po = auctionSessionService.findById(bizOrderPO.getAuctionSessionId());
                if (po.getAuctionStatus() == AuctionEnums.AuctionStatus.END) {
                    //发起退款
                    refund(bizOrderPO);
                }
                break;
            case MEMBER:
            case PARTNER:
                //修改会员类型为达人
                userService.modifyUserLevel(bizOrderPO.getUserPO().getUserId(), UserEnums.UserLevel.LEVEL_DA_REN);
                break;
            default:
                break;
        }
    }

    @Override
    public boolean enableAuction(Long userId, Long sessionId) {
        return bizOrderRepository.existsByAuctionSessionIdAndPayStatusAndUserPO_UserId(sessionId, OrderEnums.PayStatus.PAYED, userId);
    }

    @Override
    public BizOrderPO findByAuctionAndUser(Long userId, Long sessionId) {
        return bizOrderRepository.findByAuctionSessionIdAndPayStatusAndUserPO_UserId(sessionId, OrderEnums.PayStatus.PAYED, userId);
    }

    @Override
    public void refund(BizOrderPO orderPO) {
        if (!orderPO.enableRefund()) {
            log.info("退还保证金失败---当前无法执行退款，请检查订单状态");
            throw new ApiException(ApiResult.error("当前无法执行退款，请检查订单状态"));
        }
        try {
            orderPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.WAIT_SELLER_REFUND);
            //生成退款单
            RefundBillPO refundBillPO = refundBillService.create(orderPO, orderPO.getFinalAmount(), OrderEnums.RefundType.ORIGINAL_WAY);
            //发起退款
            if (refundBillPO.getPayType() == OrderEnums.PayType.WECHAT_PAY) {
                WxRefundDTO wxRefundDTO = new WxRefundDTO();
                PaymentBillPO paymentBillPO = paymentBillService.findById(orderPO.getPaymentBillId());
                wxRefundDTO.setOrderNo(paymentBillPO.getId());
                wxRefundDTO.setRefundFee(refundBillPO.getAmount());
                wxRefundDTO.setTotalFee(refundBillPO.getAmount());
                wxRefundDTO.setRefundNo(refundBillPO.getId());
                boolean success = wechatPayService.wxRefund(wxRefundDTO);
                if (!success) {
                    throw new ApiException(ApiResult.errorWith(ResultCodeEnum.THIRD_ERROR, "请求微信退款接口失败"));
                }
            }
            save(orderPO);
        } catch (Exception e) {
            log.error("业务订单退款失败---" + e.getMessage(), e);
        }
    }

    @Override
    public void refundComplete(String orderId) {
        BizOrderPO orderPO = findById(orderId);
        orderPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.COMPLETE_APPLY);

        save(orderPO);
    }

    @Override
    public BizOrderPO save(BaseSaveDTO saveDTO) {
        return null;
    }
}
